#include "FabricaRecursos.h"

#include "Buffer.h"
#include "Archivo.h"

#include "EstrategiaRecursoUnAlmacenamiento.h"
#include "EstrategiaRecursoEscrituraDirecta.h"
#include "EstrategiaRecursoEscrituraDiferida.h"

#include "EstrategiaAlmacenamientoRegistros.h"
#include "EstrategiaAlmacenamientoBloques.h"
#include "EstrategiaAlmacenamientoTexto.h"

#include "IndiceHashExtensible.h"
#include "IndiceBSharp.h"

#include "WrapperComponente.h"

#include "funciones.h"

namespace Toolkit
{

RecursoAlmacenamiento* FabricaRecursos::recursoBuffer(
		const UnidadAlmacenamiento &uAlmacenamiento,
		const unsigned long int &tamanoUnidad)
{
	TipoSerializacion tSerializacion = BINARIO;
	if (uAlmacenamiento == A_TEXTO)
	{
		tSerializacion = TEXTO;
	}

	WrapperComponente aux;

	Almacenamiento *primario = new Buffer(tamanoUnidad + aux.getTamanoMetadata(
			tSerializacion), tSerializacion);
	EstrategiaRecurso* estrategiaRecurso =
			new EstrategiaRecursoUnAlmacenamiento(tSerializacion, tamanoUnidad);

	EstrategiaAlmacenamiento *estrategiaAlmacenamiento =
			getEstrategiaAlmacenamiento(uAlmacenamiento, tamanoUnidad);

	return new RecursoAlmacenamiento(primario, estrategiaRecurso,
			estrategiaAlmacenamiento);
}

RecursoAlmacenamiento* FabricaRecursos::recursoArchivo(
		const string &rutaArchivo, const string &nombreArchivo,
		const UnidadAlmacenamiento &uAlmacenamiento,
		const unsigned long int &tamanoUnidad, const TipoIndexado &tIndexado)
{
	TipoSerializacion tSerializacion = BINARIO;
	if (uAlmacenamiento == A_TEXTO)
	{
		tSerializacion = TEXTO;
	}

	stringstream metadata;
	generarMetadata(tSerializacion, uAlmacenamiento, tIndexado,
			UN_ALMACENAMIENTO, metadata, 0);

	WrapperComponente aux;

	Almacenamiento *primario = new Archivo(tamanoUnidad
			+ aux.getTamanoMetadata(tSerializacion), rutaArchivo,
			nombreArchivo, tSerializacion, metadata);

	EstrategiaRecurso* estrategiaRecurso =
			new EstrategiaRecursoUnAlmacenamiento(tSerializacion, tamanoUnidad);

	EstrategiaAlmacenamiento *estrategiaAlmacenamiento =
			getEstrategiaAlmacenamiento(uAlmacenamiento, tamanoUnidad);

	Indice *indice = getIndice(tIndexado, uAlmacenamiento, tamanoUnidad,
			tSerializacion, rutaArchivo, nombreArchivo);

	return new RecursoAlmacenamiento(primario, estrategiaRecurso,
			estrategiaAlmacenamiento, indice);
}

RecursoAlmacenamiento *FabricaRecursos::recursoEscrituraDirecta(
		const string &rutaArchivo, const string &nombreArchivo,
		const UnidadAlmacenamiento &uAlmacenamiento,
		const unsigned long int &tamanoUnidad, const TipoIndexado &tIndexado)
{
	TipoSerializacion tSerializacion = BINARIO;
	if (uAlmacenamiento == A_TEXTO)
	{
		tSerializacion = TEXTO;
	}

	stringstream metadata;
	generarMetadata(tSerializacion, uAlmacenamiento, tIndexado,
			ESCRITURA_DIRECTA, metadata, 0);

	WrapperComponente aux;
	unsigned long int tamanoTotal = tamanoUnidad + aux.getTamanoMetadata(
			tSerializacion);

	EstrategiaRecurso* estrategiaRecurso =
			new EstrategiaRecursoEscrituraDirecta(tSerializacion, tamanoTotal);

	Almacenamiento *primario = new Archivo(tamanoTotal, rutaArchivo,
			nombreArchivo, tSerializacion, metadata);
	Almacenamiento *secundario = new Buffer(tamanoTotal, tSerializacion);

	EstrategiaAlmacenamiento *estrategiaAlmacenamiento =
			getEstrategiaAlmacenamiento(uAlmacenamiento, tamanoUnidad);

	Indice *indice = getIndice(tIndexado, uAlmacenamiento, tamanoUnidad,
			tSerializacion, rutaArchivo, nombreArchivo);

	return new RecursoAlmacenamiento(primario, estrategiaRecurso,
			estrategiaAlmacenamiento, indice, secundario);
}

RecursoAlmacenamiento *FabricaRecursos::recursoEscrituraDiferida(
		const string &rutaArchivo, const string &nombreArchivo,
		const UnidadAlmacenamiento &uAlmacenamiento,
		const unsigned long int &tamanoUnidad, const TipoIndexado &tIndexado,
		const unsigned long int &catidadElementosBuffer)
{
	TipoSerializacion tSerializacion = BINARIO;
	if (uAlmacenamiento == A_TEXTO)
	{
		tSerializacion = TEXTO;
	}

	stringstream metadata;
	generarMetadata(tSerializacion, uAlmacenamiento, tIndexado,
			ESCRITURA_DIFERIDA, metadata, catidadElementosBuffer);

	WrapperComponente auxPrimario;
	WrapperBufferDiferido auxSecundario;
	unsigned long int tamanoArchivo = tamanoUnidad
			+ auxPrimario.getTamanoMetadata(tSerializacion);
	unsigned long int tamanoBuffer = tamanoUnidad
			+ auxSecundario.getTamanoMetadata(tSerializacion);

	EstrategiaRecurso
			* estrategiaRecurso = new EstrategiaRecursoEscrituraDiferida(
					tSerializacion, tamanoUnidad);

	Almacenamiento *primario = new Archivo(tamanoArchivo, rutaArchivo,
			nombreArchivo, tSerializacion, metadata);
	Almacenamiento *secundario = new Buffer(catidadElementosBuffer,
			tamanoBuffer, tSerializacion);

	EstrategiaAlmacenamiento *estrategiaAlmacenamiento =
			getEstrategiaAlmacenamiento(uAlmacenamiento, tamanoUnidad);

	Indice *indice = getIndice(tIndexado, uAlmacenamiento, tamanoUnidad,
			tSerializacion, rutaArchivo, nombreArchivo);

	return new RecursoAlmacenamiento(primario, estrategiaRecurso,
			estrategiaAlmacenamiento, indice, secundario);
}

RecursoAlmacenamiento* FabricaRecursos::recuperarRecurso(
		const string &rutaArchivo, const string &nombreArchivo,
		const TipoSerializacion &tSerializacion)

{
	stringstream metadata;
	Archivo *primario = new Archivo(rutaArchivo, nombreArchivo, tSerializacion);
	primario->leerMetaData(metadata);

	WrapperComponente aux;
	unsigned long int tamanoUnidad = primario->getTamano()
			- aux.getTamanoMetadata(tSerializacion);

	UnidadAlmacenamiento uAlmacenamiento;
	TipoIndexado tIndexado;
	EstrategiaBuffering buffering;
	unsigned long int elementosBuffer;

	leerMetadata(tSerializacion, metadata, uAlmacenamiento, tIndexado,
			buffering, elementosBuffer);

	EstrategiaAlmacenamiento *estrategiaAlmacenamiento =
			getEstrategiaAlmacenamiento(uAlmacenamiento, tamanoUnidad);
	Indice *indice = getIndice(tIndexado, uAlmacenamiento, tamanoUnidad,
			tSerializacion, rutaArchivo, nombreArchivo, false);

	EstrategiaRecurso *estrategiaRecurso = NULL;
	Almacenamiento *secundario = NULL;

	if (buffering == ESCRITURA_DIRECTA)
	{
		estrategiaRecurso = new EstrategiaRecursoEscrituraDirecta(
				tSerializacion, tamanoUnidad);

		WrapperComponente aux;

		secundario = new Buffer(tamanoUnidad + aux.getTamanoMetadata(
				tSerializacion), tSerializacion);

	}
	else if (buffering == ESCRITURA_DIFERIDA)
	{
		estrategiaRecurso = new EstrategiaRecursoEscrituraDiferida(
				tSerializacion, tamanoUnidad);

		WrapperBufferDiferido aux;

		secundario = new Buffer(elementosBuffer, tamanoUnidad
				+ aux.getTamanoMetadata(tSerializacion), tSerializacion);

	}
	else if (buffering == UN_ALMACENAMIENTO)
	{
		estrategiaRecurso = new EstrategiaRecursoUnAlmacenamiento(
				tSerializacion, tamanoUnidad);
		secundario = NULL;
	}

	return new RecursoAlmacenamiento(primario, estrategiaRecurso,
			estrategiaAlmacenamiento, indice, secundario);

}

EstrategiaAlmacenamiento* FabricaRecursos::getEstrategiaAlmacenamiento(
		const UnidadAlmacenamiento &uAlmacenamiento,
		const unsigned long int &tamanoBloque)
{
	EstrategiaAlmacenamiento *estrategiaAlmacenamiento = NULL;

	switch (uAlmacenamiento)
	{
	case A_TEXTO:
		estrategiaAlmacenamiento = new EstrategiaAlmacenamientoTexto(
				tamanoBloque);
		break;

	case A_REGISTROS:
		estrategiaAlmacenamiento = new EstrategiaAlmacenamientoRegistros(
				tamanoBloque);
		break;

	case A_BLOQUES:
		estrategiaAlmacenamiento = new EstrategiaAlmacenamientoBloques(
				tamanoBloque);
		break;

	case A_BLOQUES_COMPRESOS:
		estrategiaAlmacenamiento = new EstrategiaAlmacenamientoBloques(
				tamanoBloque, true);
		break;
	}

	return estrategiaAlmacenamiento;
}

Indice* FabricaRecursos::getIndice(const TipoIndexado &tIndexado,
		const UnidadAlmacenamiento &uAlmacenamiento,
		const unsigned long int &tamanoUnidad,
		const TipoSerializacion &tSerializacion, const string &ruta,
		const string &nombre, const bool &nuevo)
{
	Indice* indice = NULL;
	unsigned long int tamanoBloqueIndice = 0;
	switch (tIndexado)
	{
	case SIN_INDEXADO:
		indice = NULL;
		break;

	case HASHING_EXTENSIBLE:
		if (nuevo)
		{
			if (uAlmacenamiento == A_BLOQUES || uAlmacenamiento
					== A_BLOQUES_COMPRESOS)
			{
				tamanoBloqueIndice = tamanoUnidad;
			}
			else if (uAlmacenamiento == A_REGISTROS)
			{
				tamanoBloqueIndice = tamanoUnidad * 30;
			}

			indice = new IndiceHashExtensible(tamanoBloqueIndice, ruta, nombre);
		}
		else
		{
			indice = new IndiceHashExtensible(ruta, nombre);
		}

		break;

	case B_SHARP:
		if (nuevo)
		{
			if (uAlmacenamiento == A_BLOQUES || uAlmacenamiento
					== A_BLOQUES_COMPRESOS)
			{
				tamanoBloqueIndice = tamanoUnidad;
			}
			else if ((uAlmacenamiento == A_REGISTROS) || (uAlmacenamiento
					== A_TEXTO))
			{
				tamanoBloqueIndice = tamanoUnidad * 30;
			}

			indice = new IndiceBSharp(tamanoBloqueIndice, ruta, nombre,
					tSerializacion);
		}
		else
		{
			indice = new IndiceBSharp(ruta, nombre, tSerializacion);
		}

		break;
	}

	return indice;
}

void FabricaRecursos::generarMetadata(const TipoSerializacion &tSerializacion,
		const UnidadAlmacenamiento &uAlmacenamiento,
		const TipoIndexado &tIndexado, const EstrategiaBuffering &buffering,
		stringstream &metadata, const unsigned long int &elementosBuffer)
{
	serializacionDigito(metadata, tSerializacion, (int) uAlmacenamiento);
	serializacionDigito(metadata, tSerializacion, (int) tIndexado);
	serializacionDigito(metadata, tSerializacion, (int) buffering);
	serializacionDigito(metadata, tSerializacion, elementosBuffer);

}

void FabricaRecursos::leerMetadata(const TipoSerializacion &tSerializacion,
		stringstream &metadata, UnidadAlmacenamiento &uAlmacenamiento,
		TipoIndexado &tIndexado, EstrategiaBuffering &buffering,
		unsigned long int &elementosBuffer)
{
	int iUalm, iTindex, iBuffering;

	hidratacionDigito(metadata, tSerializacion, iUalm);
	hidratacionDigito(metadata, tSerializacion, iTindex);
	hidratacionDigito(metadata, tSerializacion, iBuffering);
	hidratacionDigito(metadata, tSerializacion, elementosBuffer);

	uAlmacenamiento = (UnidadAlmacenamiento) iUalm;
	tIndexado = (TipoIndexado) iTindex;
	buffering = (EstrategiaBuffering) iBuffering;

}

}
